package com.netkit;

import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.Channels;

import com.netkit.event.NetkitSessionListener;
import com.netkit.exception.SessionException;
import com.netkit.logger.Logger;
import com.netkit.message.Message;

public class Session
  implements MessageSendable
{
  private static final Logger LOGGER = Logger.getLogger(Session.class);
  private NetkitContext context;
  private Channel channel;
  private final Map<String, Object> attributeMap = new HashMap();
  private NetkitSessionListener onCreatedListener = null;
  private NetkitSessionListener onClosedListener = null;

  private long lastCommunicationTime = System.currentTimeMillis();
  private final MessageDispatcher dispatcher = new MessageDispatcher();

  Session(NetkitContext context, Channel channel) {
    this.context = context;
    this.channel = channel;
    ChannelFuture future = channel.getCloseFuture();
    future.addListener(new ChannelClosedListener());
  }

  public NetkitContext getContext() {
    return this.context;
  }

  public void setAttribute(String key, Object val) {
    this.attributeMap.put(key, val);
  }

  public Object getAttribute(String key) {
    return this.attributeMap.get(key);
  }

  public Set<String> getAttributeNames() {
    return this.attributeMap.keySet();
  }

  public Object removeAttribute(String key) {
    return this.attributeMap.remove(key);
  }

  public int getSessionId() {
    return this.channel.getId().intValue();
  }

  public Future sendMessage(Message message) {
    onSendMessage(message);
    if (!this.channel.isConnected()) {
      throw new SessionException("The channel was closed,cannot write message.");
    }
    ChannelFuture future = Channels.write(this.channel, message);
    return new Future(this, future);
  }

  public Future sendMessageAndClose(Message message) {
    ChannelFuture future = Channels.write(this.channel, message);
    future.addListener(ChannelFutureListener.CLOSE);
    return new Future(this, future);
  }

  public SocketAddress getRemoteAddress() {
    return this.channel.getRemoteAddress();
  }

  public SocketAddress getLocalAddress() {
    return this.channel.getLocalAddress();
  }

  public Channel getChannel() {
    return this.channel;
  }

  public Future close() {
    ChannelFuture future = this.channel.close();
    return new Future(this, future);
  }

  public void setOnCreatedEventListener(NetkitSessionListener listener)
  {
    this.onCreatedListener = listener;
  }

  public void setOnClosedEventListener(NetkitSessionListener listener) {
    this.onClosedListener = listener;
  }

  public NetkitSessionListener getOnCreatedEventListener() {
    return this.onCreatedListener;
  }

  public NetkitSessionListener getOnClosedEventListener() {
    return this.onClosedListener;
  }

  public boolean isClosed() {
    return !this.channel.isOpen();
  }

  public boolean isConnected() {
    return this.channel.isConnected();
  }

  public boolean isSendable() {
    return this.channel.isWritable();
  }

  public long getLastCommunicationTime() {
    return this.lastCommunicationTime;
  }

  public MessageTask createMessageTask(MessageDispatcher dispatcher, Message message) {
    return MessageTask.newInstance(dispatcher, this, message);
  }

  public void onSendMessage(Message message) {
    this.lastCommunicationTime = System.currentTimeMillis();
  }

  public void onRecvMessage(Message message) {
    this.lastCommunicationTime = System.currentTimeMillis();
    MessageTask task = createMessageTask(this.dispatcher, message);

    getContext().getWorkerExecutor().submit(task);
  }

  protected void onCreated() {
    try {
      if (getOnCreatedEventListener() != null)
        getOnCreatedEventListener().onEvent(this);
    }
    catch (Exception e) {
      LOGGER.warn(e.getMessage(), e);
    }
  }

  protected void onClosed() {
    try {
      if (getOnClosedEventListener() != null)
        getOnClosedEventListener().onEvent(this);
    }
    catch (Exception e) {
      LOGGER.warn(e.getMessage(), e);
    }
    this.attributeMap.clear();
  }

  class ChannelClosedListener
    implements ChannelFutureListener
  {
    ChannelClosedListener()
    {
    }

    public void operationComplete(ChannelFuture future)
      throws Exception
    {
      Session.this.onClosed();
    }
  }
}